/**
 * <p>Title: Hero.java</p>
 * <p>Description: CS 343 Final Project. This class contains the key elements and aspects of the hero</p>
 * @author Tim Olmstead, Brady Fetting, Erik Brown, Thales Kirk
 * @email timothy.olmstead@my.uwrf.edu, brady.fetting@my.uwrf.edu, 
 * 			erik.brown@my.uwrf.edu, thales.kirk@my.uwrf.edu
 * @date November 15, 2013
 */

package Charactors;

import java.awt.Image;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import Components.SoundEffects;
import Components.Speed;
import GameState.Game;
import Settings.Config;

public class Hero {
	boolean alive = true;
	private int xLoc;
	private int yLoc;
	private double xSpeed = 2;
	private double ySpeed = 0;
	private double deltaX = 0;
	private int deltaY = 0;
	private final int STATIC = 0;
	private final int LEFT = 1;
	private final int RIGHT = 2;
	private final int JUMP = 3;
	private int state;
	private BufferedImage allSprites;
	private Image[] leftSprite, rightSprite, staticSprite, jumpSprite,
			deadSprite;
	private int staticSpriteLoc, leftSpriteLoc, rightSpriteLoc, jumpSpriteLoc,
			deadSpriteLoc;
	private long spriteTime = 100l;
	private long lastSpriteDraw;
	private int heroHeight;
	private int heroWidth;
	private Speed speed;

	/**
	 * @param x
	 * @param y
	 * @description creating the hero to appear in game along with the
	 *              corresponding sprite to the hero's action
	 */
	public Hero(int x, int y) {
		xLoc = x;
		yLoc = y;
		lastSpriteDraw = System.currentTimeMillis();
		try {
			allSprites = ImageIO.read(new File(
					"src/images/segwayGuySpriteSheetv3.png"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (allSprites == null)
			return;
		int totalWidth = allSprites.getWidth(null);
		int totalHeight = allSprites.getHeight(null);
		leftSprite = new Image[4];
		rightSprite = new Image[4];
		staticSprite = new Image[4];
		jumpSprite = new Image[4];
		deadSprite = new Image[4];
		staticSpriteLoc = 0;
		leftSpriteLoc = 0;
		rightSpriteLoc = 0;
		jumpSpriteLoc = 0;
		deadSpriteLoc = 0;
		for (int i = 0; i < 4; i++) {
			staticSprite[i] = allSprites.getSubimage((totalWidth / 4) * i, 0,
					totalWidth / 4, totalHeight / 5);
			leftSprite[i] = allSprites.getSubimage((totalWidth / 4) * i,
					(totalHeight / 5), totalWidth / 4, totalHeight / 5);
			rightSprite[i] = allSprites.getSubimage((totalWidth / 4) * i,
					(totalHeight / 5) * 2, totalWidth / 4, totalHeight / 5);
			jumpSprite[i] = allSprites.getSubimage((totalWidth / 4) * i,
					(totalHeight / 5) * 3, totalWidth / 4, totalHeight / 5);
			deadSprite[i] = allSprites.getSubimage((totalWidth / 4) * i,
					(totalHeight / 5) * 4, totalWidth / 4, totalHeight / 5);
		}
		heroHeight = staticSprite[0].getHeight(null);
		heroWidth = staticSprite[0].getWidth(null);
		speed = new Speed();
	}

	/**
	 * @param none
	 * @description gets the speed of the sprite
	 */
	public Speed getSpeed() {
		return speed;
	}

	// Using Rectangles to border your hero for collision
	/**
	 * @param none
	 * @description using rectangles to border the hero for collisions
	 */
	public Rectangle getBounds() {
		return new Rectangle(getxLoc(), getyLoc(), heroWidth, heroHeight);
	}

	// End bounding hero

	/**
	 * @param none
	 * @description gets the xLoc of the hero sprite
	 */
	public synchronized int getxLoc() {
		return xLoc;
	}

	/**
	 * @param xLoc
	 * @description sets the xLoc of the hero sprite
	 */
	public void setxLoc(int xLoc) {
		this.xLoc = xLoc;
	}

	/**
	 * @param none
	 * @description gets the yLoc of the hero sprite
	 */
	public synchronized int getyLoc() {
		return yLoc;
	}

	/**
	 * @param yLoc
	 * @description sets the yLoc of the hero sprite
	 */
	public void setyLoc(int yLoc) {
		this.yLoc = yLoc;
	}

	/**
	 * @param none
	 * @description gets the value of DeltaX
	 */
	public double getDeltaX() {
		return deltaX;
	}

	/**
	 * @param deltaX
	 * @description sets the value of DeltaX
	 */
	private void setDeltaX(double deltaX) {
		this.deltaX = deltaX;
	}

	/**
	 * @param none
	 * @description gets the value of DeltaY
	 */
	public int getDeltaY() {
		return deltaY;
	}

	/**
	 * @param deltaY
	 * @description sets the value of DeltaY
	 */
	private void setDeltaY(int deltaY) {
		this.deltaY = deltaY;
	}

	/**
	 * @param none
	 * @description sends a value of -1 to DeltaY
	 */
	public void up() {
		setDeltaY(-1);
	}

	/**
	 * @param none
	 * @description sends a value of 1 to DeltaY
	 */
	public void down() {
		setDeltaY(1);
	}

	/**
	 * @param none
	 * @description when called, sprite will move left and slow down his speed
	 */
	public void left() {
		if (Game.getGameSpeed() > 3) {
			setDeltaX(-1.5);
			return;
		}
		setDeltaX(-1);
	}

	/**
	 * @param none
	 * @description when called, sprite will move right and increase his speed
	 */
	public void right() {
		if (Game.getGameSpeed() > 3) {
			setDeltaX(1.2);
			return;
		}
		setDeltaX(1);
	}

	/**
	 * @param none
	 * @description sets DeltaX to 0, indicating sprite is not moving left/right
	 */
	public void stopX() {
		setDeltaX(0);
	}

	/**
	 * @param none
	 * @description sets DeltaY to 0, indicating sprite is not moving up/down
	 */
	public void stopY() {
		setDeltaY(0);
	}

	/**
	 * @param none
	 * @description gets the ySpeed of the sprite
	 */
	public double getySpeed() {
		return ySpeed;
	}

	/**
	 * @param ySpeed
	 * @description sets the ySpeed of the price
	 */
	public void setySpeed(double ySpeed) {
		this.ySpeed = ySpeed;
	}

	/**
	 * @param none
	 * @description gets the height of the hero
	 **/
	public int getHeroHeight() {
		return heroHeight;
	}

	/**
	 * @param heroHeight
	 * @description sets the hero height
	 */
	public void setHeroHeight(int heroHeight) {
		this.heroHeight = heroHeight;
	}

	/**
	 * @param none
	 * @description gets the width of the hero
	 **/
	public int getHeroWidth() {
		return heroWidth;
	}

	/**
	 * @param heroWidth
	 * @description sets the hero width
	 */
	public void setHeroWidth(int heroWidth) {
		this.heroWidth = heroWidth;
	}

	/**
	 * @param none
	 * @description controls the movement of the hero based on values gathered
	 */
	public void move() {
		if (yLoc == Game.getGroundLevel())
			xSpeed = 2;
		else
			xSpeed = 4;
		if (yLoc < Game.getGroundLevel()) {
			fall();
		}

		// limit the bounds of where the player can move to

		// right limit
		if (deltaX > 0 && xLoc < Config.FRAME_WIDTH - heroWidth - 25)
			xLoc += deltaX * xSpeed;
		// left limit
		else if (deltaX < 0 && xLoc > 25)
			xLoc += deltaX * xSpeed;

		yLoc += ySpeed;
	}

	/**
	 * @param none
	 * @description when called, this will tell the sprite to jump and allow to
	 *              get over obstacles
	 */
	public void jump() {
		if (yLoc < Game.getGroundLevel())
			return;
		(new SoundEffects()).playSpringSound();
		ySpeed = -9.5;

	}

	/**
	 * @param none
	 * @description when called alive is set to false and this will play the
	 *              death sound of the hero sprite
	 */
	public void die() {
		if (!alive)
			return;
		alive = false;
		speed.setSpeed(0);
		(new SoundEffects()).playDeathSound();
	}

	/**
	 * @param none
	 * @description returns if the hero is still alive or not
	 */
	public boolean isAlive() {
		return alive;
	}

	/**
	 * @param none
	 * @description fall method which is essentially gravity, if hero is above
	 *              ground, they will fall
	 */
	public void fall() {
		ySpeed += Game.gavity;
		if (ySpeed > Game.terminalVelocity) {
			ySpeed = Game.terminalVelocity;
		}
		// yLoc += ySpeed;
		if (yLoc + ySpeed >= Game.getGroundLevel()) {
			ySpeed = 0;
			yLoc = Game.getGroundLevel();
		}

	}

	/**
	 * @param none
	 * @description gets the polygon border for the corresponding sprite
	 *              situation
	 */
	public Polygon getPolygon() {
		switch (state) {
		case LEFT:
			// return left sprite polygon
			return leftPolygon();
		case RIGHT:
			// return right sprite polygon
			return rightPolygon();
		case JUMP:
			// return jump sprite polygon
			return jumpPolygon();
		default:
			// return static sprite polygon
			return staticPolygon();
		}
	}

	/**
	 * @param none
	 * @description sets the polygon for the left moving sprite for collision
	 *              detection
	 */
	private Polygon leftPolygon() {
		int[] xs = new int[8];
		int[] ys = new int[8];
		xs[0] = xLoc + 20;
		ys[0] = yLoc + 17;
		xs[1] = xLoc + 46;
		ys[1] = yLoc + 11;
		xs[2] = xLoc + 51;
		ys[2] = yLoc + 57;
		xs[3] = xLoc + 66;
		ys[3] = yLoc + 64;
		xs[4] = xLoc + 75;
		ys[4] = yLoc + 110;
		xs[5] = xLoc + 68;
		ys[5] = yLoc + 122;
		xs[6] = xLoc + 21;
		ys[6] = yLoc + 122;
		xs[7] = xLoc + 16;
		ys[7] = yLoc + 113;
		return new Polygon(xs, ys, xs.length);
	}

	/**
	 * @param none
	 * @description sets the polygon for the right moving sprite for collision
	 *              detection
	 */
	private Polygon rightPolygon() {
		int[] xs = new int[8];
		int[] ys = new int[8];
		xs[0] = xLoc + 62;
		ys[0] = yLoc + 14;
		xs[1] = xLoc + 87;
		ys[1] = yLoc + 13;
		xs[2] = xLoc + 87;
		ys[2] = yLoc + 33;
		xs[3] = xLoc + 74;
		ys[3] = yLoc + 61;
		xs[4] = xLoc + 84;
		ys[4] = yLoc + 76;
		xs[5] = xLoc + 69;
		ys[5] = yLoc + 122;
		xs[6] = xLoc + 22;
		ys[6] = yLoc + 123;
		xs[7] = xLoc + 16;
		ys[7] = yLoc + 114;
		return new Polygon(xs, ys, xs.length);
	}

	/**
	 * @param none
	 * @description sets the polygon for the jumping sprite for collision
	 *              detection
	 */
	private Polygon jumpPolygon() {
		int[] xs = new int[8];
		int[] ys = new int[8];
		xs[0] = xLoc + 31;
		ys[0] = yLoc + 14;
		xs[1] = xLoc + 49;
		ys[1] = yLoc + 6;
		xs[2] = xLoc + 66;
		ys[2] = yLoc + 17;
		xs[3] = xLoc + 73;
		ys[3] = yLoc + 69;
		xs[4] = xLoc + 75;
		ys[4] = yLoc + 124;
		xs[5] = xLoc + 71;
		ys[5] = yLoc + 133;
		xs[6] = xLoc + 22;
		ys[6] = yLoc + 133;
		xs[7] = xLoc + 17;
		ys[7] = yLoc + 124;
		return new Polygon(xs, ys, xs.length);
	}

	/**
	 * @param none
	 * @description sets the polygon for the static sprite for collision
	 *              detection
	 */
	private Polygon staticPolygon() {
		int[] xs = new int[8];
		int[] ys = new int[8];
		xs[0] = xLoc + 30;
		ys[0] = yLoc + 10;
		xs[1] = xLoc + 65;
		ys[1] = yLoc + 10;
		xs[2] = xLoc + 58;
		ys[2] = yLoc + 60;
		xs[3] = xLoc + 70;
		ys[3] = yLoc + 68;
		xs[4] = xLoc + 72;
		ys[4] = yLoc + 114;
		xs[5] = xLoc + 65;
		ys[5] = yLoc + 123;
		xs[6] = xLoc + 22;
		ys[6] = yLoc + 123;
		xs[7] = xLoc + 16;
		ys[7] = yLoc + 111;
		return new Polygon(xs, ys, xs.length);
	}

	/**
	 * @param none
	 * @description gets and returns the animation of the sprite based on the
	 *              heros actions
	 */
	public Image getImage() {
		if (!alive) {
			if (System.currentTimeMillis() > lastSpriteDraw + spriteTime) {// wait
																			// to
																			// show
																			// spite...
				deadSpriteLoc++;
				lastSpriteDraw = System.currentTimeMillis();
				deadSpriteLoc = deadSpriteLoc % 4;
			}
			return deadSprite[deadSpriteLoc];
		}
		if (yLoc < Game.getGroundLevel()) {
			// to match up with collision detection. We want what we see to be
			// what we collide with
			state = JUMP;
			if (System.currentTimeMillis() > lastSpriteDraw + spriteTime) {// wait
																			// to
																			// show
																			// spite...
				jumpSpriteLoc++;
				lastSpriteDraw = System.currentTimeMillis();
				jumpSpriteLoc = jumpSpriteLoc % 4;
			}
			return jumpSprite[jumpSpriteLoc];
		} else if (deltaX < 0) {
			// to match up with collision detection. We want what we see to be
			// what we collide with
			state = LEFT;
			if (System.currentTimeMillis() > lastSpriteDraw + spriteTime) { // wait
																			// to
																			// show
																			// spite...
				leftSpriteLoc++;
				lastSpriteDraw = System.currentTimeMillis();
				leftSpriteLoc = leftSpriteLoc % 4;
			}
			return leftSprite[leftSpriteLoc];
		} else if (deltaX > 0) {
			// to match up with collision detection. We want what we see to be
			// what we collide with
			state = RIGHT;
			if (System.currentTimeMillis() > lastSpriteDraw + spriteTime) {// wait
																			// to
																			// show
																			// spite...
				lastSpriteDraw = System.currentTimeMillis();
				rightSpriteLoc++;
				rightSpriteLoc = rightSpriteLoc % 4;
			}
			return rightSprite[rightSpriteLoc];
		} else {
			// to match up with collision detection. We want what we see to be
			// what we collide with
			state = STATIC;
			if (System.currentTimeMillis() > lastSpriteDraw + spriteTime) {// wait
																			// to
																			// show
																			// spite...
				staticSpriteLoc++;
				lastSpriteDraw = System.currentTimeMillis();
				staticSpriteLoc = staticSpriteLoc % 4;
			}
			return staticSprite[staticSpriteLoc];
		}
	}

}
